////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Michiel Helvensteijn                                    //
//                                                                            //
// This file is part of the Mist compiler.                                    //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modif   //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// along with the Mist compiler.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////////

#include "visitors/echovisitor.hpp"

/******************************************************************************
 * Includes                                                                   *
 ******************************************************************************/

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

#include <sstream>
using std::ostringstream;

#include <boost/shared_ptr.hpp>
using boost::dynamic_pointer_cast;

#include <typeinfo>

#include "ast/nodetypes.hpp"
#include "visitors/visitor.hpp"
#include "codestream.hpp"
#include "options.hpp"
#include "assert.hpp"

/******************************************************************************
 * EchoVisitor class                                                          *
 ******************************************************************************/

class EchoVisitor : public Visitor {
	public:
		
		EchoVisitor(ostream& stream, unsigned tabSize);
		
		~EchoVisitor();
		
		void visit(shared_ptr<ArithmeticOperation>   node);
		void visit(shared_ptr<FunctionCall>          node);
		void visit(shared_ptr<UnaryOperation>        node);
		void visit(shared_ptr<Identifier>            node);
		void visit(shared_ptr<IntegerLiteral>        node);
		void visit(shared_ptr<BooleanLiteral>        node);
		void visit(shared_ptr<Null>                  node);
		void visit(shared_ptr<ArrayType>             node);
		void visit(shared_ptr<IntegerType>           node);
		void visit(shared_ptr<BooleanType>           node);
		void visit(shared_ptr<Assertion>             node);
		void visit(shared_ptr<IfStatement>           node);
		void visit(shared_ptr<WhileStatement>        node);
		void visit(shared_ptr<Assignment>            node);
		void visit(shared_ptr<IdAssignment>          node);
		void visit(shared_ptr<CompoundStatement>     node);
		void visit(shared_ptr<FunctionType>          node);
		void visit(shared_ptr<VoidType>              node);
		void visit(shared_ptr<VariableDeclaration>   node);
		void visit(shared_ptr<FunctionDeclaration>   node);
		void visit(shared_ptr<Declarations>          node);
		void visit(shared_ptr<Subscripting>          node);
		void visit(shared_ptr<ComparisonChain>       node);
		void visit(shared_ptr<TypeError>             node);
		void visit(shared_ptr<AndChain>              node);
		void visit(shared_ptr<OrChain>               node);
		void visit(shared_ptr<ImpliesChain>          node);
		void visit(shared_ptr<ForStatement>          node);
		void visit(shared_ptr<Range>                 node);
		void visit(shared_ptr<RangeType>             node);
		void visit(shared_ptr<Reference>             node);
		void visit(shared_ptr<BooleanPointerLiteral> node);
		void visit(shared_ptr<IntegerPointerLiteral> node);
	
	private:
		
		bool need_brackets(shared_ptr<FunctionCall> a);
		bool need_brackets(shared_ptr<Subscripting> a);
		bool need_brackets(shared_ptr<UnaryOperation> a);
		bool need_brackets_left(shared_ptr<ArithmeticOperation> a);
		bool need_brackets_right(shared_ptr<ArithmeticOperation> a);
		bool need_brackets(shared_ptr<ComparisonChain> a, unsigned int index);
		bool need_brackets(shared_ptr<AndChain> node, unsigned int index);
		bool need_brackets(shared_ptr<OrChain> node, unsigned int index);
		bool need_brackets(shared_ptr<ImpliesChain> node, unsigned int index);
		bool need_brackets_left(shared_ptr<Assignment> a);
		bool need_brackets_right(shared_ptr<Assignment> a);
		bool need_brackets_left(shared_ptr<IdAssignment> a);
		bool need_brackets_right(shared_ptr<IdAssignment> a);
	
	private:
		
		CodeStream& cdout;
};
/******************************************************************************
 * Function Implementations                                                   *
 ******************************************************************************/

echo::echo(shared_ptr<Node> tree, unsigned tabSize) : _tree(tree), _tabSize(tabSize) {
	assert(tree);
}

echo::operator string() const {
	ostringstream sout;
	
	sout << *this;
	
	return sout.str();
}

ostream& echo::send_to_stream(ostream& out) const {
	shared_ptr<EchoVisitor> visitor(new EchoVisitor(out, _tabSize));
	
	_tree->accept(visitor);
	
	return out;
}

/******************************************************************************/

EchoVisitor::EchoVisitor(ostream& out, unsigned tabSize) : cdout(*new CodeStream(out, tabSize)) {
} // intentionally empty

EchoVisitor::~EchoVisitor() {
	delete &cdout;
}

void EchoVisitor::visit(shared_ptr<ArithmeticOperation> node) {
	assert(node);
	
	if (need_brackets_left(node)) {
		cdout << '(';
		node->left_operand()->accept(shared_from_this());
		cdout << ')';
	} else {
		node->left_operand()->accept(shared_from_this());
	}
	
	cdout << ' ' << to_string(node->operator_type()) << ' ';
	
	if (need_brackets_right(node)) {
		cdout << '(';
		node->right_operand()->accept(shared_from_this());
		cdout << ')';
	} else {
		node->right_operand()->accept(shared_from_this());
	}
}

void EchoVisitor::visit(shared_ptr<FunctionCall> node) {
	assert(node);
	
	if (need_brackets(node)) {
		cdout << '(';
		node->base()->accept(shared_from_this());
		cdout << ')';
	} else {
		node->base()->accept(shared_from_this());
	}
	
	cdout << '(';
	for (unsigned int i = 0; i < node->parameter_count(); ++i) {
		node->parameters()[i]->accept(shared_from_this());
		if (i < node->parameter_count() - 1)
			cdout << ", ";
	}
	cdout << ')';
}

void EchoVisitor::visit(shared_ptr<UnaryOperation> node) {
	assert(node);
	
	cdout << to_string(node->operator_type());
	if (need_brackets(node)) {
		cdout << '(';
		node->operand()->accept(shared_from_this());
		cdout << ')';
	} else {
		node->operand()->accept(shared_from_this());
	}
}

void EchoVisitor::visit(shared_ptr<Identifier> node) {
	assert(node);
	
	cdout << node->text();
}

void EchoVisitor::visit(shared_ptr<IntegerLiteral> node) {
	assert(node);
	
	cdout << node->value();
}

void EchoVisitor::visit(shared_ptr<BooleanLiteral> node) {
	assert(node);
	
	cdout << (node->value() ? "true" : "false");
}

void EchoVisitor::visit(shared_ptr<Null> node) {
	assert(node);
	
	cdout << "null";
}

void EchoVisitor::visit(shared_ptr<ArrayType> node) {
	assert(node);
	
	node->content_type()->accept(shared_from_this());
	cdout << '[';
	node->size()->accept(shared_from_this());
	cdout << ']';
	if (node->weak_pointer())
		cdout << "^";
}

void EchoVisitor::visit(shared_ptr<IntegerType> node) {
	assert(node);
	
	cdout << "int";
	if (node->weak_pointer())
		cdout << "^";
}

void EchoVisitor::visit(shared_ptr<BooleanType> node) {
	assert(node);
	
	cdout << "bool";
	if (node->weak_pointer())
		cdout << "^";
}

void EchoVisitor::visit(shared_ptr<Assertion> node) {
	assert(node);
	
	cdout << "@ ";
	if (node->condition_is_set())
		node->condition()->accept(shared_from_this());
	else
		node->variable_declaration()->accept(shared_from_this());
}

void EchoVisitor::visit(shared_ptr<IfStatement> node) {
	assert(node);
	
	// Condition
	cdout << "if (";
	node->condition()->accept(shared_from_this());
	cdout << ')';
	
	// Then statement
	if (typeid(*node->then_statement()) == typeid(CompoundStatement)) {
		cdout << ' ';
		node->then_statement()->accept(shared_from_this());
	} else {
		cdout << '\n' << indent;
		node->then_statement()->accept(shared_from_this());
		if (   typeid(*node->then_statement()) == typeid(Assignment)
		    || typeid(*node->then_statement()) == typeid(IdAssignment)
		    || typeid(*node->then_statement()) == typeid(FunctionCall)
		    || typeid(*node->then_statement()) == typeid(VariableDeclaration)) {
			cdout << ';';
		}
		cdout << unindent;
	}
	
	// Else statement
	if (node->else_statement_is_set()) {
		if (typeid(*node->then_statement()) == typeid(CompoundStatement))
			cdout << " else";
		else
			cdout << "\nelse";
		if (   typeid(*node->else_statement()) == typeid(CompoundStatement)
		    || typeid(*node->else_statement()) == typeid(IfStatement)) {
			cdout << ' ';
			node->else_statement()->accept(shared_from_this());
		} else {
			cdout << '\n' << indent;
			node->else_statement()->accept(shared_from_this());
			if (   typeid(*node->else_statement()) == typeid(Assignment)
			    || typeid(*node->else_statement()) == typeid(IdAssignment)
			    || typeid(*node->else_statement()) == typeid(FunctionCall)
			    || typeid(*node->else_statement()) == typeid(VariableDeclaration)) {
				cdout << ';';
			}
			cdout << unindent;
		}
	}
}

void EchoVisitor::visit(shared_ptr<WhileStatement> node) {
	assert(node);
	
	// Condition
	cdout << "while (";
	node->condition()->accept(shared_from_this());
	cdout << ")";
	
	// Statement
	if (typeid(*node->statement()) == typeid(CompoundStatement)) {
		cdout << ' ';
		node->statement()->accept(shared_from_this());
	} else {
		cdout << '\n' << indent;
		node->statement()->accept(shared_from_this());
		if (   typeid(*node->statement()) == typeid(Assignment)
		    || typeid(*node->statement()) == typeid(IdAssignment)
		    || typeid(*node->statement()) == typeid(FunctionCall)
		    || typeid(*node->statement()) == typeid(VariableDeclaration)) {
			cdout << ';';
		}
		cdout << unindent;
	}
}

void EchoVisitor::visit(shared_ptr<Assignment> node) {
	assert(node);
	
	if (need_brackets_left(node)) {
		cdout << '(';
		node->left_side()->accept(shared_from_this());
		cdout << ')';
	} else {
		node->left_side()->accept(shared_from_this());
	}
	
	cdout << " <- ";
	
	if (need_brackets_right(node)) {
		cdout << '(';
		node->right_side()->accept(shared_from_this());
		cdout << ')';
	} else {
		node->right_side()->accept(shared_from_this());
	}
}

void EchoVisitor::visit(shared_ptr<IdAssignment> node) {
	assert(node);
	
	if (need_brackets_left(node)) {
		cdout << '(';
		node->left_side()->accept(shared_from_this());
		cdout << ')';
	} else {
		node->left_side()->accept(shared_from_this());
	}
	
	cdout << " <-- ";
	
	if (need_brackets_right(node)) {
		cdout << '(';
		node->right_side()->accept(shared_from_this());
		cdout << ')';
	} else {
		node->right_side()->accept(shared_from_this());
	}
}

void EchoVisitor::visit(shared_ptr<CompoundStatement> node) {
	assert(node);
	
	if (node->name_is_set()) {
		node->name()->accept(shared_from_this());
		cdout << " ";
	}
	
	cdout << "{\n" << indent;
	foreach (shared_ptr<Statement> statement, node->statements()) {
		statement->accept(shared_from_this());
		if (   typeid(*statement) == typeid(Assignment)
		    || typeid(*statement) == typeid(IdAssignment)
		    || typeid(*statement) == typeid(FunctionCall)
		    || typeid(*statement) == typeid(VariableDeclaration)) {
			cdout << ';';
		}
		cdout << "\n";
	}
	cdout << unindent << "}";
}

void EchoVisitor::visit(shared_ptr<FunctionType> node) {
	assert(node);
	
	cdout << "(";
	node->return_type()->accept(shared_from_this());
	cdout << ")" << "function" << "(";
	for (unsigned int i = 0; i < node->parameter_type_count(); ++i) {
		cdout << node->parameter_types()[i].policy->name << " ";
		node->parameter_types()[i].type->accept(shared_from_this());
		if (i < node->parameter_type_count() - 1)
			cdout << ", ";
	}
	cdout << ")";
	if (node->weak_pointer())
		cdout << "^";
}

void EchoVisitor::visit(shared_ptr<VoidType> node) {
	assert(node);
	
	cdout << "void";
}

void EchoVisitor::visit(shared_ptr<VariableDeclaration> node) {
	assert(node);
	
	node->type()->accept(shared_from_this());
	cdout << ' ';
	for (unsigned int i = 0; i < node->variable_count(); ++i) {
		node->variables()[i].name->accept(shared_from_this());
		if (node->variables()[i].is_initialized()) {
			if (node->variables()[i].init_is_identity)
				cdout << " <-- ";
			else
				cdout << " <- ";
			node->variables()[i].init->accept(shared_from_this());
		}
		if (i < node->variable_count() - 1) {
			cdout << ", ";
		}
	}
}

void EchoVisitor::visit(shared_ptr<FunctionDeclaration> node) {
	assert(node);
	
	// The signature
	node->type()->return_type()->accept(shared_from_this());
	cdout << ' ';
	node->name()->accept(shared_from_this());
	cdout << "(";
	for (unsigned int i = 0; i < node->parameter_name_count(); ++i) {
		if (*node->type()->parameter_types()[i].policy != *ParameterPolicy::ByValue)
			cdout << node->type()->parameter_types()[i].policy->name << " ";
		node->type()->parameter_types()[i].type->accept(shared_from_this());
		cdout << " ";
		node->parameter_names()[i]->accept(shared_from_this());
		if (i < node->parameter_name_count() - 1)
			cdout << ", ";
	}
	cdout << ")";
	
	// The precondition
	if (node->precondition_count() > 0) {
		cdout << "\npre {\n" << indent;
		foreach (shared_ptr<Assertion> assertion, node->preconditions())
			assertion->accept(shared_from_this());
		cdout << unindent << "\n}\nbody";
	}
	
	// The body
	cdout << ' ';
	node->statement()->accept(shared_from_this());
	
	// The postcondition
	if (node->postcondition_count() > 0) {
		cdout << "\npost {\n" << indent;
		foreach (shared_ptr<Assertion> assertion, node->postconditions())
			assertion->accept(shared_from_this());
		cdout << unindent << "\n}";
	}
}

void EchoVisitor::visit(shared_ptr<Declarations> node) {
	assert(node);
	
	for (unsigned int i = 0; i < node->declaration_count(); ++i) {
		node->declarations()[i]->accept(shared_from_this());
		if (typeid(*node->declarations()[i]) == typeid(VariableDeclaration))
			cdout << ';';
		cdout << '\n';
		if (i < node->declaration_count() - 1)
			cdout << '\n';
	}
}

void EchoVisitor::visit(shared_ptr<Subscripting> node) {
	assert(node);
	
	if (need_brackets(node)) {
		cdout << '(';
		node->base()->accept(shared_from_this());
		cdout << ')';
	} else {
		node->base()->accept(shared_from_this());
	}
	cdout << '[';
	node->subscript()->accept(shared_from_this());
	cdout << ']';
}

void EchoVisitor::visit(shared_ptr<ComparisonChain> node) {
	assert(node);
	
	for (unsigned int i = 0; i < node->comparison_operator_count(); ++i) {
		if (need_brackets(node, i)) {
			cdout << '(';
			node->operands()[i]->accept(shared_from_this());
			cdout << ')';
		} else {
			node->operands()[i]->accept(shared_from_this());
		}
		cdout << ' ' << to_string(node->comparison_operators()[i]) << ' ';
	}
	if (need_brackets(node, node->comparison_operator_count())) {
		cdout << '(';
		node->operands()[node->comparison_operator_count()]->accept(shared_from_this());
		cdout << ')';
	} else {
		node->operands()[node->comparison_operator_count()]->accept(shared_from_this());
	}
}

void EchoVisitor::visit(shared_ptr<TypeError> node) {
	assert(node);
	
	cdout << "<type-error>";
}

void EchoVisitor::visit(shared_ptr<AndChain> node) {
	assert(node);
	
	if (need_brackets(node, 0)) {
		cdout << '(';
		node->operands()[0]->accept(shared_from_this());
		cdout << ')';
	} else {
		node->operands()[0]->accept(shared_from_this());
	}
	for (unsigned i = 1; i < node->operand_count(); ++i) {
		cdout << " and ";
		if (need_brackets(node, i)) {
			cdout << '(';
			node->operands()[i]->accept(shared_from_this());
			cdout << ')';
		} else {
			node->operands()[i]->accept(shared_from_this());
		}
	}
}

void EchoVisitor::visit(shared_ptr<OrChain> node) {
	assert(node);
	
	if (need_brackets(node, 0)) {
		cdout << '(';
		node->operands()[0]->accept(shared_from_this());
		cdout << ')';
	} else {
		node->operands()[0]->accept(shared_from_this());
	}
	for (unsigned i = 1; i < node->operand_count(); ++i) {
		cdout << " or ";
		if (need_brackets(node, i)) {
			cdout << '(';
			node->operands()[i]->accept(shared_from_this());
			cdout << ')';
		} else {
			node->operands()[i]->accept(shared_from_this());
		}
	}
}

void EchoVisitor::visit(shared_ptr<ImpliesChain> node) {
	assert(node);
	
	if (need_brackets(node, 0)) {
		cdout << '(';
		node->operands()[0]->accept(shared_from_this());
		cdout << ')';
	} else {
		node->operands()[0]->accept(shared_from_this());
	}
	for (unsigned i = 1; i < node->operand_count(); ++i) {
		cdout << " => ";
		if (need_brackets(node, i)) {
			cdout << '(';
			node->operands()[i]->accept(shared_from_this());
			cdout << ')';
		} else {
			node->operands()[i]->accept(shared_from_this());
		}
	}
}

void EchoVisitor::visit(shared_ptr<ForStatement> node) {
	assert(node);
	
	// Condition
	cdout << "for (";
	node->iterator()->accept(shared_from_this());
	cdout << ", ";
	node->range()->accept(shared_from_this());
	cdout << ")";
	
	// Statement
	if (typeid(*node->statement()) == typeid(CompoundStatement)) {
		cdout << ' ';
		node->statement()->accept(shared_from_this());
	} else {
		cdout << '\n' << indent;
		node->statement()->accept(shared_from_this());
		if (   typeid(*node->statement()) == typeid(Assignment)
		    || typeid(*node->statement()) == typeid(IdAssignment)
		    || typeid(*node->statement()) == typeid(FunctionCall)
		    || typeid(*node->statement()) == typeid(VariableDeclaration)) {
			cdout << ';';
		}
		cdout << unindent;
	}
}

void EchoVisitor::visit(shared_ptr<Range> node) {
	assert(node);
	
	node->lower_bound()->accept(shared_from_this());
	cdout << "..";
	node->upper_bound()->accept(shared_from_this());
	if (!node->step()->equals(IntegerLiteral(1))) {
		cdout << "..";
		node->step()->accept(shared_from_this());
	}
}

void EchoVisitor::visit(shared_ptr<RangeType> node) {
	assert(node);
	
	node->type()->accept(shared_from_this());
	cdout << "..";
	if (node->weak_pointer())
		cdout << "^";
}

void EchoVisitor::visit(shared_ptr<Reference> node) {
	assert(node);
	
	cdout << node->path();
}

void EchoVisitor::visit(shared_ptr<BooleanPointerLiteral> node) {
	assert(node);
	
	cdout << "bool(";
	node->value()->accept(shared_from_this());
	cdout << ")";
}

void EchoVisitor::visit(shared_ptr<IntegerPointerLiteral> node) {
	assert(node);
	
	cdout << "int(";
	node->value()->accept(shared_from_this());
	cdout << ")";
}

/******************************************************************************/

bool EchoVisitor::need_brackets(shared_ptr<FunctionCall> node) {
	assert(node);
	
	if (   typeid(*node->base()) == typeid(UnaryOperation)
	    || typeid(*node->base()) == typeid(ArithmeticOperation)
	    || typeid(*node->base()) == typeid(ComparisonChain)
	    || typeid(*node->base()) == typeid(AndChain)
	    || typeid(*node->base()) == typeid(OrChain)
	    || typeid(*node->base()) == typeid(ImpliesChain)
	    || typeid(*node->base()) == typeid(Assignment)
	    || typeid(*node->base()) == typeid(IdAssignment))
		return true;
	
	return false;
}

bool EchoVisitor::need_brackets(shared_ptr<Subscripting> node) {
	assert(node);
	
	if (   typeid(*node->base()) == typeid(UnaryOperation)
	    || typeid(*node->base()) == typeid(ArithmeticOperation)
	    || typeid(*node->base()) == typeid(ComparisonChain)
	    || typeid(*node->base()) == typeid(AndChain)
	    || typeid(*node->base()) == typeid(OrChain)
	    || typeid(*node->base()) == typeid(ImpliesChain)
	    || typeid(*node->base()) == typeid(Assignment)
	    || typeid(*node->base()) == typeid(IdAssignment))
		return true;
	
	return false;
}

bool EchoVisitor::need_brackets(shared_ptr<UnaryOperation> node) {
	assert(node);
	
	if (   typeid(*node->operand()) == typeid(ArithmeticOperation)
	    || typeid(*node->operand()) == typeid(ComparisonChain)
	    || typeid(*node->operand()) == typeid(AndChain)
	    || typeid(*node->operand()) == typeid(OrChain)
	    || typeid(*node->operand()) == typeid(ImpliesChain)
	    || typeid(*node->operand()) == typeid(Assignment)
	    || typeid(*node->operand()) == typeid(IdAssignment))
		return true;
	
	return false;
}

bool EchoVisitor::need_brackets_left(shared_ptr<ArithmeticOperation> node) {
	assert(node);
	
	if (   typeid(*node->left_operand()) == typeid(ComparisonChain)
	    || typeid(*node->left_operand()) == typeid(AndChain)
	    || typeid(*node->left_operand()) == typeid(OrChain)
	    || typeid(*node->left_operand()) == typeid(ImpliesChain)
	    || typeid(*node->left_operand()) == typeid(Assignment)
	    || typeid(*node->left_operand()) == typeid(IdAssignment))
		return true;
	
	return false;
}

bool EchoVisitor::need_brackets_right(shared_ptr<ArithmeticOperation> node) {
	assert(node);
	
	if (   typeid(*node->right_operand()) == typeid(ArithmeticOperation)
	       && !higher_precedence(dynamic_pointer_cast<ArithmeticOperation>(node->right_operand())->operator_type(), node->operator_type())
	    || typeid(*node->left_operand()) == typeid(ComparisonChain)
	    || typeid(*node->left_operand()) == typeid(AndChain)
	    || typeid(*node->left_operand()) == typeid(OrChain)
	    || typeid(*node->left_operand()) == typeid(ImpliesChain)
	    || typeid(*node->left_operand()) == typeid(Assignment)
	    || typeid(*node->left_operand()) == typeid(IdAssignment))
		return true;
	
	return false;
}

bool EchoVisitor::need_brackets(shared_ptr<ComparisonChain> node, unsigned int index) {
	assert(node);
	
	if (   typeid(*node->operands()[index]) == typeid(ComparisonChain)
	    || typeid(*node->operands()[index]) == typeid(AndChain)
	    || typeid(*node->operands()[index]) == typeid(OrChain)
	    || typeid(*node->operands()[index]) == typeid(ImpliesChain)
	    || typeid(*node->operands()[index]) == typeid(Assignment)
	    || typeid(*node->operands()[index]) == typeid(IdAssignment))
		return true;
	
	return false;
}

bool EchoVisitor::need_brackets(shared_ptr<AndChain> node, unsigned int index) {
	assert(node);
	
	if (   typeid(*node->operands()[index]) == typeid(OrChain)
	    || typeid(*node->operands()[index]) == typeid(ImpliesChain)
	    || typeid(*node->operands()[index]) == typeid(Assignment)
	    || typeid(*node->operands()[index]) == typeid(IdAssignment))
		return true;
	
	return false;
}

bool EchoVisitor::need_brackets(shared_ptr<OrChain> node, unsigned int index) {
	assert(node);
	
	if (   typeid(*node->operands()[index]) == typeid(ImpliesChain)
	    || typeid(*node->operands()[index]) == typeid(Assignment)
	    || typeid(*node->operands()[index]) == typeid(IdAssignment))
		return true;
	
	return false;
}

bool EchoVisitor::need_brackets(shared_ptr<ImpliesChain> node, unsigned int index) {
	assert(node);
	
	if (   typeid(*node->operands()[index]) == typeid(ImpliesChain)
	    || typeid(*node->operands()[index]) == typeid(Assignment)
	    || typeid(*node->operands()[index]) == typeid(IdAssignment))
		return true;
	
	return false;
}

bool EchoVisitor::need_brackets_left(shared_ptr<Assignment> node) {
	assert(node);
	
	return (   typeid(*node->left_side()) == typeid(Assignment)
	        || typeid(*node->left_side()) == typeid(IdAssignment));
}

bool EchoVisitor::need_brackets_right(shared_ptr<Assignment> node) {
	assert(node);
	
	return false;
}

bool EchoVisitor::need_brackets_left(shared_ptr<IdAssignment> node) {
	assert(node);
	
	return (   typeid(*node->left_side()) == typeid(Assignment)
	        || typeid(*node->left_side()) == typeid(IdAssignment));
}

bool EchoVisitor::need_brackets_right(shared_ptr<IdAssignment> node) {
	assert(node);
	
	return false;
}
